#include "JctoHex.h"
#include "hxPrintf.h"
#include <stdio.h>
#include <memory.h>

static uint8_t decode(char c)
{
    return (c>='0'&&c<='9')?(c-'0'):((c>='a'&&c<='z')?(c-'a'+0xa):(c-'A'+0XA));
}

static char encode(uint8_t c)
{
    return (c>=0&&c<=9)?(c+'0'):(c-0XA+'A');
}

static uint8_t getHex(FILE*fp,uint64_t*sum)
{
    const char c1=fgetc(fp);
    const char c2=fgetc(fp);
    const uint8_t data=(decode(c1)<<4)|(decode(c2));
    if(sum){(*sum)+=data;}
    return data;
}

static void printHex(FILE*fp,uint64_t*sum,uint8_t data)
{
    const char c1=encode((data>>4)&0XF);
    const char c2=encode((data)&0XF);
    fputc(c1,fp);
    fputc(c2,fp);
    if(sum){(*sum)+=data;}
}

int jctoHexImport(FILE*fp,uint8_t*flash,uint64_t flashSize,uint64_t addrList[32][2])
{
    memset(flash,0X00,flashSize);
    addrList[0][0]=addrList[0][1]=0;
    int addrListCnt=0;
    for(uint64_t ext=0,base=0;;)
    {
        while(':'!=(fgetc(fp))){}
        uint64_t sum=0;
        const uint8_t len=getHex(fp,&sum);
        const uint64_t addr=((getHex(fp,&sum)<<8)|(getHex(fp,&sum)))+(base<<16)+(ext<<4);
        const uint8_t type=getHex(fp,&sum);
        if(type==0)
        {
            if(addrList[addrListCnt][0]==addrList[addrListCnt][1])
            {
                addrList[addrListCnt][0]=addr;
                addrList[addrListCnt][1]=addr+len;
            }
            else if(addrList[addrListCnt][1]==addr)
            {
                addrList[addrListCnt][1]=addr+len;
            }
            else
            {
                ++addrListCnt;
                addrList[addrListCnt][0]=addr;
                addrList[addrListCnt][1]=addr+len;
            }
            for(int i=0;i<len;++i)
            {
                if(addr+i>=flashSize)
                {
                    return 0;
                }
                flash[addr+i]=getHex(fp,&sum);
            }
            if(len!=16)
            {
                ++addrListCnt;
                addrList[addrListCnt][0]=addr+len;
                addrList[addrListCnt][1]=addr+len;
            }
        }
        else if(type==1)
        {
            break;
        }
        else if(type==2)
        {
            ext=((getHex(fp,&sum)<<8)|(getHex(fp,&sum)));
        }
        else if(type==4)
        {
            ext=0;
            base=((getHex(fp,&sum)<<8)|(getHex(fp,&sum)));
        }
        else
        {
            hxPrintfFL("len=%2d addr=%#06X type=%d\n",len,addr,type);
            return 0;
        }
        const uint8_t check=getHex(fp,NULL);
        if((((0X100-(sum&0XFF)))&0XFF)!=check)
        {
            hxPrintfFL("check filed\n");
            return 0;
        }
    }
    return addrListCnt+((addrList[addrListCnt][1]!=addrList[addrListCnt][0])?1:0);
}

static void writeBaseExt(FILE*fp,uint64_t base,uint8_t type)
{
    uint64_t sum=0;
    fputc(':',fp);
    printHex(fp,&sum,2);
    printHex(fp,&sum,0);
    printHex(fp,&sum,0);
    printHex(fp,&sum,type);
    printHex(fp,&sum,((base)>>8)&0XFF);
    printHex(fp,&sum,((base))&0XFF);
    printHex(fp,&sum,(((0X100-(sum&0XFF)))&0XFF));
    fputc('\r',fp);
    fputc('\n',fp);
}

void jctoHexExport(FILE*fp,uint8_t*flash,const uint64_t addrList[32][2],int addrListCnt)
{
    uint64_t ext=0,base=0;
    for(int i=0;i<addrListCnt;++i)
    {
        if(addrList[i][0]>>16!=base)
        {
            base=addrList[i][0]>>16;
            ext=0;
            writeBaseExt(fp,base,4);
            writeBaseExt(fp,ext,2);
        }
        for(uint64_t now_addr=addrList[i][0];now_addr<addrList[i][1];)
        {
            if((now_addr>>16)!=(((base<<16)+(ext<<4))>>16))
            {
                ext=((0XFFFF0000&(now_addr-(base<<16)))>>4);
                writeBaseExt(fp,ext,2);
            }
            {
                uint64_t len=addrList[i][1]-now_addr;
                if(len>16){len=16;}
                fputc(':',fp);
                uint64_t sum=0;
                printHex(fp,&sum,len);
                printHex(fp,&sum,((now_addr-(base<<16)-(ext<<4))>>8)&0XFF);
                printHex(fp,&sum,((now_addr-(base<<16)-(ext<<4)))&0XFF);
                printHex(fp,&sum,0);
                for(int k=0;k<len;++k,++now_addr){printHex(fp,&sum,flash[now_addr]);}
                printHex(fp,&sum,(((0X100-(sum&0XFF)))&0XFF));
                fputc('\r',fp);
                fputc('\n',fp);
            }
        }
    }
    {
        uint64_t sum=0;
        fputc(':',fp);
        printHex(fp,&sum,0);
        printHex(fp,&sum,0);
        printHex(fp,&sum,0);
        printHex(fp,&sum,1);
        printHex(fp,&sum,(((0X100-(sum&0XFF)))&0XFF));
        fputc('\r',fp);
        fputc('\n',fp);
    }
}
